﻿using Newtonsoft.Json;
using OBS.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using up6.db.biz;
using up6.db.utils;
using up6.utils;
using up6.sql.model;

namespace up6.db.model
{
    public class FileInf
    {
        /// <summary>
        /// 6.3版本新增字段
        /// </summary>
        [DataBase("f_id")]
        public string id = string.Empty;
        /// <summary>
        /// 文件夹ID
        /// </summary>
        [DataBase("f_pid")]
        public string pid = string.Empty;
        /// <summary>
        /// 根级文件夹ID
        /// </summary>
        [DataBase("f_pidRoot")]
        public string pidRoot = string.Empty;
        /// <summary>
        /// 表示当前项是否是一个文件夹项。
        /// </summary>
        [DataBase("f_fdTask")]
        public bool fdTask = false;
        /// <summary>
        /// 与xdb_folders.fd_id对应
        /// </summary>
        /// <summary>
        /// 是否是文件夹中的子文件
        /// </summary>
        [DataBase("f_fdChild")]
        public bool fdChild = false;
        /// <summary>
        /// 用户ID。与第三方系统整合使用。
        /// </summary>
        [DataBase("f_uid")]
        public int uid = 0;
        /// <summary>
        /// 文件在本地电脑中的名称。
        /// </summary>
        [DataBase("f_nameLoc")]
        public string nameLoc = string.Empty;
        /// <summary>
        /// 文件在服务器中的名称。
        /// </summary>
        [DataBase("f_nameSvr")]
        public string nameSvr = string.Empty;
        /// <summary>
        /// 文件在本地电脑中的完整路径。示例：D:\Soft\QQ2012.exe
        /// </summary>
        [DataBase("f_pathLoc")]
        public string pathLoc = string.Empty;
        /// <summary>
        /// 文件在服务器中的完整路径。示例：F:\ftp\uer\md5.exe
        /// </summary>
        [DataBase("f_pathSvr")]
        public string pathSvr = string.Empty;
        /// <summary>
        /// 文件在服务器中的相对路径。示例：/www/web/upload/md5.exe
        /// </summary>
        [DataBase("f_pathRel")]
        public string pathRel = string.Empty;
        /// <summary>
        /// 文件MD5
        /// </summary>
        [DataBase("f_md5")]
        public string md5 = string.Empty;
        /// <summary>
        /// 数字化的文件长度。以字节为单位，示例：120125
        /// 文件大小可能超过2G，所以使用long
        /// </summary>
        [DataBase("f_lenLoc")]
        public long lenLoc = 0;
        /// <summary>
        /// 本地文件加密后的大小
        /// </summary>
        [DataBase("f_lenLocSec")]
        public long lenLocSec = 0;
        /// <summary>
        /// 格式化的文件尺寸。示例：10.03MB
        /// </summary>
        [DataBase("f_sizeLoc")]
        public string sizeLoc = string.Empty;
        /// <summary>
        /// 文件续传位置。
        /// 文件大小可能超过2G，所以使用long
        /// </summary>
        [DataBase("f_offset")]
        public long offset = 0;
        /// <summary>
        /// 已上传大小。以字节为单位
        /// 文件大小可能超过2G，所以使用long
        /// </summary>
        [DataBase("f_lenSvr")]
        public long lenSvr = 0;
        /// <summary>
        /// 已上传百分比。示例：10%
        /// </summary>
        [DataBase("f_perSvr")]
        public string perSvr = "0%";
        [DataBase("f_complete")]
        public bool complete = false;
        [DataBase("f_time")]
        public DateTime time = DateTime.Now;
        [DataBase("f_deleted")]
        public bool deleted = false;
        /// <summary>
        /// 是否已经扫描完毕，提供给大型文件夹使用
        /// 大型文件夹上传完毕后开始扫描
        /// </summary>
        [DataBase("f_scan")]
        public bool scaned = false;
        /// <summary>
        /// 块索引，基于1
        /// </summary>
        [DataBase("f_blockIndex")]
        public int blockIndex = 0;
        /// <summary>
        /// 块偏移，基于整个文件
        /// </summary>
        [DataBase("f_blockOffset")]
        public long blockOffset = 0;
        /// <summary>
        /// 块总数
        /// </summary>
        [DataBase("f_blockCount")]
        public int blockCount = 0;
        /// <summary>
        /// 块大小
        /// </summary>
        [DataBase("f_blockSize")]
        public int blockSize = 0;

        /// <summary>
        /// 用于第三方存储的对象ID，如FastDFS,Minio等
        /// </summary>
        public String object_id = "";

        /// <summary>
        /// 对象存储的key
        /// </summary>
        [DataBase("f_object_key")]
        public String object_key = "";
        //
        public String etag = "";
        /// <summary>
        /// 文件是否加密
        /// </summary>
        [DataBase("f_encrypt")]
        public bool encrypt = false;

        /// <summary>
        /// 计算文件加密后的大小，16字节对齐
        /// </summary>
        public void calLenLocSec() {
            long a = this.lenLoc % 16;
            this.lenLocSec = this.lenLoc + (16 - (a > 0 ? a : 16));
        }

        public string schemaFile()
        {
            return Path.Combine( this.parentDir(),"schema.txt");
        }

        /**
         * 生成AWS S3文件key
         * D:\Soft\guid\QQ.exe => /guid/QQ.exe
         * @return
         */
        public String S3Key()
        {
            //格式 /guid/QQ.exe
            String key = PathTool.combin("", this.id);
            key = PathTool.combin(key, this.nameLoc);
            return key;
        }

        public string ossKey()
        {
            //格式：guid/QQ.exe
            return PathTool.combin(this.id, this.nameLoc);
        }

        public string getObjectKey() {
            if (ConfigReader.storage() == StorageType.Minio) return this.S3Key();
            else if (ConfigReader.storage() == StorageType.OSS||
                ConfigReader.storage() == StorageType.OBS) return this.ossKey();
            //fastdfs
            return this.object_id;
        }

        /**
         * 返回AWS s3，ETag保存文件
         * 路径：dir/guid/etags.txt
         * @return
         */
        public String ETagsFile()
        {
            String f = PathTool.combin(this.parentDir(), this.id);
            f = PathTool.combin(f, "etags.txt");
            return f;
        }

        public string parentDir()
        {
            return PathTool.parentDir(this.pathSvr);
        }

        public void saveScheme()
        {
            //仅第一块保存
            if (1!=this.blockIndex ) return;
            //仅保存子文件数据
            if (string.IsNullOrEmpty(this.pid)) return;
            
            FolderSchema fs = new FolderSchema();
            this.calLenLocSec();//自动计算文件加密后的大小
            fs.addFile(this);
        }

        /**
         * 保存块ID信息
         * 路径：
         * D:/Soft/guid/etags.txt
         */
        public void saveEtags()
        {
            FileEtag fe = new FileEtag();
            fe.saveTags(this);
        }

        /**
         * <CompleteMultipartUpload xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
           <Part>
              <ChecksumCRC32>string</ChecksumCRC32>
              <ChecksumCRC32C>string</ChecksumCRC32C>
              <ChecksumSHA1>string</ChecksumSHA1>
              <ChecksumSHA256>string</ChecksumSHA256>
              <ETag>string</ETag>
              <PartNumber>integer</PartNumber>
           </Part>
           ...
        </CompleteMultipartUpload>

         * @return
         */

        public String etags()
        {
            List<FileInf> blocks = new List<FileInf>();
            //加载/guid/etags.txt
            StreamReader sr = new StreamReader(this.ETagsFile());
            string line = String.Empty;
            Dictionary<int, bool> dic = new Dictionary<int, bool>();
            while ((line = sr.ReadLine()) != null)
            {
                Console.WriteLine(line);
                var b = JsonConvert.DeserializeObject<FileInf>(line);
                //防止重复添加
                if (dic.ContainsKey(b.blockIndex)) continue;
                blocks.Add(b);
                dic.Add(b.blockIndex, true);
            }

            XmlDocument doc = new XmlDocument();
            var root = doc.CreateElement("CompleteMultipartUpload");
            foreach (FileInf b in blocks)
            {
                var part = doc.CreateElement("Part");
                var etag = doc.CreateElement("ETag");
                var number = doc.CreateElement("PartNumber");

                number.InnerText = b.blockIndex.ToString();
                etag.InnerText = b.etag;
                part.AppendChild(etag);
                part.AppendChild(number);
                root.AppendChild(part);
            }
            doc.AppendChild(root);

            return UtilsTool.xmlToString(ref doc);
        }

        public List<UploadPartResponse> obsEtags() {

            List<FileInf> blocks = new List<FileInf>();
            //加载/guid/etags.txt
            StreamReader sr = new StreamReader(this.ETagsFile());
            string line = String.Empty;
            Dictionary<int, bool> dic = new Dictionary<int, bool>();
            while ((line = sr.ReadLine()) != null)
            {
                Console.WriteLine(line);
                var b = JsonConvert.DeserializeObject<FileInf>(line);
                //防止重复添加
                if (dic.ContainsKey(b.blockIndex)) continue;
                blocks.Add(b);
                dic.Add(b.blockIndex, true);
            }

            List<UploadPartResponse> etags = new List<UploadPartResponse>();
            foreach (FileInf b in blocks)
            {
                etags.Add(JsonConvert.DeserializeObject<UploadPartResponse>( b.etag));
            }
            return etags;
        }

        public List<Aliyun.OSS.PartETag> ossEtags()
        {
            List<FileInf> blocks = new List<FileInf>();
            //加载/guid/etags.txt
            StreamReader sr = new StreamReader(this.ETagsFile());
            string line = String.Empty;
            Dictionary<int, bool> dic = new Dictionary<int, bool>();
            while ((line = sr.ReadLine()) != null)
            {
                Console.WriteLine(line);
                var b = JsonConvert.DeserializeObject<FileInf>(line);
                //防止重复添加
                if (dic.ContainsKey(b.blockIndex)) continue;
                blocks.Add(b);
                dic.Add(b.blockIndex, true);
            }

            List<Aliyun.OSS.PartETag> etags = new List<Aliyun.OSS.PartETag>();
            foreach (FileInf b in blocks)
            {
                Aliyun.OSS.PartETag et = new Aliyun.OSS.PartETag(b.blockIndex,b.etag);
                etags.Add(et);
            }
            return etags;
        }

        /// <summary>
        /// 块路径。
        /// folder/file.id/blockindex.part
        /// </summary>
        /// <returns></returns>
        public string blockPath()
        {
            var ps = PathTool.parentDir(this.pathSvr);
            ps = string.Format("{0}/{1}/{2}.part",
                ps,
                this.id,
                this.blockIndex);
            return ps;
        }

        public string formatSize(long byteCount)
        {
            string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
            if (byteCount == 0)
                return "0" + suf[0];
            long bytes = Math.Abs(byteCount);
            int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
            double num = Math.Round(bytes / Math.Pow(1024, place), 1);
            return (Math.Sign(byteCount) * num).ToString() + suf[place];
        }
    }
}
